using System;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using Autofac;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using uTILLIty.Rave.Contract.Configuration;
using uTILLIty.Rave.Contract.Exception;
using uTILLIty.Rave.Contract.StorageAdapter;
using uTILLIty.Rave.Core.Strategy;

#pragma warning disable 612,618
namespace uTILLIty.Rave.Core.StorageAdapter
{
	[TestClass]
	public class NtfsStorageAdapterTests : TestBase
	{
		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAclsTest()
		{
			var di = new DirectoryInfo(@".\");
			var acl = di.GetAccessControl(AccessControlSections.Access);
			var accessRules = acl.GetAccessRules(true, true, typeof(SecurityIdentifier));

			foreach (FileSystemAccessRule rule in accessRules)
			{
				var ntAccount = (NTAccount)rule.IdentityReference.Translate(typeof(NTAccount));
				// ReSharper disable LocalizableElement
				TestContext.WriteLine("Identity: {5} ({0})\n\tInheritance: {1}\n\tPropagation: {2}\n\tRights: {3}\n\tAccess-Type: {4}\n\n",
					// ReSharper restore LocalizableElement
					rule.IdentityReference, rule.InheritanceFlags, rule.PropagationFlags, rule.FileSystemRights, rule.AccessControlType,
					ntAccount.Value);
			}
		}

		[TestMethod, Ignore]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectoryAcrossDomain()
		{
			const string domain = "domain.local";
			var target = GetNtfsStorageAdapter();
			var member = new Member("Administrator", "Administrator", "displayName", MemberType.User);
			var additionalInformation = new MemberAdditionalInformation
																		{
																			LdapConnectionString = "LDAP://" + domain,
																		};
			string filter = target.BuildLdapSearchFilter(member, additionalInformation);
			var result = target.QueryActiveDirectory(member, filter, additionalInformation);
			TestContext.WriteLine("Search yields {0} results", result.Count);
			Assert.AreNotEqual(0, result.Count);
			foreach (SearchResult entry in result)
			{
				var dn = entry.Properties["distinguishedname"][0].ToString();
				TestContext.WriteLine("\ncn = {0}", entry.Properties["cn"][0]);
				TestContext.WriteLine("\n\tdistinguishedname = {0}", dn);
			}
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectoryInvalidLdap()
		{
			var target = GetNtfsStorageAdapter();
			var member = new Member("a", "a", "sn", MemberType.User);
			var additionalInformation = new MemberAdditionalInformation { LdapConnectionString = "123" };
			string filter = target.BuildLdapSearchFilter(member, additionalInformation);
			try
			{
				target.QueryActiveDirectory(member, filter, additionalInformation);
			}
			catch (ConfigurationErrorException ex) //BUG in VS2012 ExpectedExceptionMessageAttribute
			{
				Assert.AreEqual("The LDAP connection string supplied ('123') is invalid (it must begin with ldap://)", ex.Message);
				return; //passed
			}
			Assert.Fail("No Exception raised");
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectory()
		{
			var target = GetNtfsStorageAdapter();
			var name = Environment.GetEnvironmentVariable("USERNAME");
			var member = new Member(name, name, "SamAccountName", MemberType.User);
			string filter = target.BuildLdapSearchFilter(member, null);
			var result = target.QueryActiveDirectory(member, filter, null);
			Assert.AreNotEqual(0, result.Count);
			TestContext.WriteLine("{0}\n", result[0].Path);
			TestContext.WriteLine("{0}\n", result[0].Properties["manager"][0]);
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectoryCurrentUserTypeUnknown()
		{
			var target = GetNtfsStorageAdapter();
			var name = Environment.GetEnvironmentVariable("USERNAME");
			var member = new Member(name, name, "SamAccountName", MemberType.Unknown);
			string filter = target.BuildLdapSearchFilter(member, null);
			var result = target.QueryActiveDirectory(member, filter, null);
			Assert.AreNotEqual(0, result.Count);
			TestContext.WriteLine("{0}\n", result[0].Path);
			TestContext.WriteLine("{0}\n", result[0].Properties["manager"][0]);
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectoryCurrentUserAsGroup()
		{
			var target = GetNtfsStorageAdapter();
			var name = Environment.GetEnvironmentVariable("USERNAME");
			var member = new Member(name, name, "SamAccountName", MemberType.Group);
			string filter = target.BuildLdapSearchFilter(member, null);
			var result = target.QueryActiveDirectory(member, filter, null);
			Assert.AreEqual(0, result.Count);
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectoryUnknownLdap()
		{
			var target = GetNtfsStorageAdapter();
			var member = new Member("Doe", "Doe", "sn", MemberType.User);
			var additionalInformation = new MemberAdditionalInformation { LdapConnectionString = "LDAP://DC=dont,DC=exist" };
			string filter = target.BuildLdapSearchFilter(member, additionalInformation);

			try
			{
				target.QueryActiveDirectory(member, filter, additionalInformation);
			}
			catch (ConfigurationErrorException ex) //BUG in VS2012 ExpectedExceptionMessageAttribute
			{
				Assert.AreEqual("Could not connect to LDAP Server specified 'LDAP://DC=dont,DC=exist'", ex.Message);
				return; //passed
			}
			Assert.Fail("No Exception raised");
		}

		[TestMethod]
		[DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void QueryActiveDirectorySupplyBadCredentials()
		{
			var target = GetNtfsStorageAdapter();
			var member = new Member("Doe", "Doe", "sn", MemberType.User);
			var additionalInformation = new MemberAdditionalInformation
																		{
																			LdapConnectionUser = "xxx",
																			LdapConnectionPassword = "123"
																		};
			string filter = target.BuildLdapSearchFilter(member, additionalInformation);

			try
			{
				target.QueryActiveDirectory(member, filter, additionalInformation);
			}
			catch (ConfigurationErrorException ex) //BUG in VS2012 ExpectedExceptionMessageAttribute
			{
				Assert.AreEqual("The provided LDAP credentials for member 'Doe {Doe}' were not accepted by the LDAP server", ex.Message);
				return; //passed
			}
			Assert.Fail("No Exception raised");
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAttributeValueDateTimeUtc()
		{
			var target = GetNtfsStorageAdapter();
			var attribute = new ActiveDirectoryAttribute("DateTimeUTC", "2010-03-15T15:30", ActiveDirectoryAttributeValueType.DateTimeUTC);
			var expected = new DateTime(2010, 3, 15, 15, 30, 0, DateTimeKind.Utc);
			object actual = target.GetAttributeValue(attribute);
			Assert.AreEqual(expected, actual);
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAttributeValueInteger()
		{
			var target = GetNtfsStorageAdapter();
			var attribute = new ActiveDirectoryAttribute("Integer", "1928", ActiveDirectoryAttributeValueType.Integer);
			const int expected = 1928;
			object actual = target.GetAttributeValue(attribute);
			Assert.AreEqual(expected, actual);
		}

		[TestMethod]
		public void GetAttributeValueLongInt()
		{
			var target = GetNtfsStorageAdapter();
			var attribute = new ActiveDirectoryAttribute("LongInt", "19289393844", ActiveDirectoryAttributeValueType.LongInt);
			const long expected = 19289393844;
			object actual = target.GetAttributeValue(attribute);
			Assert.AreEqual(expected, actual);
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAttributeValueString()
		{
			var target = GetNtfsStorageAdapter();
			const string expected = "Welcome";
			var attribute = new ActiveDirectoryAttribute("String", expected, ActiveDirectoryAttributeValueType.String);
			object actual = target.GetAttributeValue(attribute);
			Assert.AreEqual(expected, actual);
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAttributeValueBinary()
		{
			var target = GetNtfsStorageAdapter();
			var attribute = new ActiveDirectoryAttribute("Binary", EncodeBase64("ABC"), ActiveDirectoryAttributeValueType.Binary);
			object actual = target.GetAttributeValue(attribute);
			Assert.IsInstanceOfType(actual, typeof(byte[]));
			var bytes = (byte[])actual;
			Assert.AreEqual(3, bytes.Length);
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void GetAttributeValueMember()
		{
			var target = GetNtfsStorageAdapter();
			StorageAdapter = MockRepository.GenerateMock<IStorageAdapter>();
			StorageAdapter.Stub(s => s.TryResolveMember(((DefaultConfigurationApplicatorStrategy)target.MemberResolver)
				.ApplicatorContext.Configuration.Members.Single(m => m.ID == "Admins")))
				.Return(GetLdapInfo(target, AdminsStorageMember));
			((DefaultConfigurationApplicatorStrategy) target.MemberResolver).ApplicatorContext.StorageAdapter = StorageAdapter;

			var attribute = new ActiveDirectoryAttribute("Member", "Admins", ActiveDirectoryAttributeValueType.Member);
			object actual = target.GetAttributeValue(attribute);
			TestContext.WriteLine("CN: {0}\n", actual);
			Assert.IsTrue(actual.ToString().StartsWith("CN="));
		}

		[TestMethod, DeploymentItem("Samples\\ConfigurationApplicator.xml")]
		public void DecodeBase64StringTest()
		{
			var target = GetNtfsStorageAdapter();
			const string expected = "This is an <encoded> Text!";
			string input = EncodeBase64(expected);
			TestContext.WriteLine("Base64 Encoded: {0}", input);
			string actual = target.DecodeBase64String(input);
			TestContext.WriteLine("Decoded: {0}", actual);
			Assert.AreEqual(expected, actual);
		}

		private string EncodeBase64(string data)
		{
			byte[] encDataByte = System.Text.Encoding.UTF8.GetBytes(data);
			string encodedData = Convert.ToBase64String(encDataByte);
			return encodedData;
		}

		private NtfsStorageAdapter GetNtfsStorageAdapter()
		{
			var adapter = new NtfsStorageAdapter { TraceSource = new TraceSource("UnitTestTraceAdapter") };
			adapter.TraceSource.Listeners.Add(new ConsoleTraceListener());
			adapter.MemberResolver = Container.ResolveNamed<IConfigurationApplicatorStrategy>(WellKnownStrategies.Default);
			var context = CreateUnitTestContext();
			((DefaultConfigurationApplicatorStrategy)adapter.MemberResolver).ApplicatorContext = context;
			return adapter;
		}

	}
}
#pragma warning restore 612,618
